# -*- coding: utf-8 -*-
import numpy as np

from qa_diagnosis.neoapp import query_detailed_disease_list_by_symptom, query_detailed_disease_list_by_disease

decay_weight = 0.16

# 某个疾病已经达到突出的概率，则切换到该疾病进行问诊，如果后续问诊的疾病概率再次低于此概率值，切回原先问诊
threshold_activate_freq = -0.3
threshold_deactivate_freq = -0.5

# 某个疾病已经达到符合的最低概率，则会自动结束问诊
# 后期需要结合符合的最低概率与香农熵来判断是否要自动结束问诊
threshold_top_off_min_freq = -0.1


class Diagnosis:

    def __init__(self):
        self.disease_name_list = None
        self.disease_freq_list = None
        self.detailed_disease_list = None
        self.inquiry_list = None
        self.latest_inquired_symptom = None
        self.latest_inquired_property = None
        self.latest_submit_option = None
        self.max_likelihood_symptom = None
        self.max_likelihood_property = None
        self.max_likelihood_option_list = None
        self.prominent_detailed_disease_list = None

    def entrance(self,
                 disease_name_list,
                 disease_freq_list,
                 detailed_disease_list,
                 prominent_detailed_disease_list,
                 inquiry_list):
        """
        入口程序
        """
        self.inquiry_list = inquiry_list

        if detailed_disease_list is None:
            self.latest_inquired_symptom = inquiry_list[0][0]
            # 初始化参数列表
            self.init_prob_list()
        else:
            self.disease_name_list = disease_name_list
            self.disease_freq_list = disease_freq_list
            self.detailed_disease_list = detailed_disease_list
            self.prominent_detailed_disease_list = prominent_detailed_disease_list
            latest_inquiry_list = inquiry_list[-1]
            self.latest_inquired_symptom = latest_inquiry_list[0]
            if len(latest_inquiry_list) > 1:
                self.latest_inquired_property = latest_inquiry_list[1]
                self.latest_submit_option = latest_inquiry_list[2]

        # 根据用户提交的选项的进行概率计算，即匹配的疾病概率进行增益，不匹配的疾病概率进行削减
        self.prob_decay_or_increase()
        # 根据计算后的概率，判断是否需要切换回从原先的症状问诊
        self.detailed_list_switch()
        # 删除已经提问过的两个候选表
        self.clear_details()
        # 继续询问用户概率最大的那个选项
        self.get_max_likelihood_inquiry()

    def get_max_likelihood_disease(self):
        disease_freq_list = self.disease_freq_list
        disease_name_list = self.disease_name_list

        disease_freq_list = disease_freq_list.reshape(1, -1)
        max_freq_index = np.argmax(disease_freq_list, axis=1)[0]
        max_likelihood_disease = disease_name_list[max_freq_index]
        return max_likelihood_disease

    def get_detailed_disease_list(self):
        detailed_disease_list = self.prominent_detailed_disease_list
        if detailed_disease_list is None:
            detailed_disease_list = self.detailed_disease_list
        return detailed_disease_list

    def get_max_likelihood_inquiry(self):

        disease_name_list = self.disease_name_list

        detailed_disease_list = self.get_detailed_disease_list()

        disease_param_max_sum_list = [0] * len(disease_name_list)
        disease_param_max_index_list = [0] * len(disease_name_list)

        for index, detailed_disease_item in enumerate(detailed_disease_list):

            disease = detailed_disease_item['disease']
            param_sum = self.get_param_sum(detailed_disease_item)

            cur_disease_index = disease_name_list.index(disease)
            if param_sum > disease_param_max_sum_list[cur_disease_index]:
                disease_param_max_sum_list[cur_disease_index] = param_sum
                disease_param_max_index_list[cur_disease_index] = index

        max_likelihood_disease = self.get_max_likelihood_disease()
        disease_index = disease_name_list.index(max_likelihood_disease)

        max_likelihood_index = disease_param_max_index_list[disease_index]
        max_likelihood_dict = detailed_disease_list[max_likelihood_index]

        max_likelihood_symptom = max_likelihood_dict['symptom']
        max_likelihood_property = max_likelihood_dict['property']
        self.inquiry_symptom_property(max_likelihood_symptom,
                                      max_likelihood_property,
                                      detailed_disease_list)

    def is_symptom_first_inquired(self, symptom):
        inquired_symptom_property_list = self.get_inquired_symptom_property_list()
        for inquired_item in inquired_symptom_property_list:
            if inquired_item['symptom'] == symptom:
                break
        else:
            return True
        return False

    def inquiry_symptom_property(self, max_likelihood_symptom,
                                 max_likelihood_property,
                                 detailed_disease_list):
        max_likelihood_option_list = []

        if self.is_symptom_first_inquired(max_likelihood_symptom) is True:
            max_likelihood_property = None
            max_likelihood_option_list.append('是')
            max_likelihood_option_list.append('否')
        else:
            for detailed_disease_item in detailed_disease_list:
                if detailed_disease_item['symptom'] == max_likelihood_symptom:
                    if detailed_disease_item['property'] == max_likelihood_property:
                        option = detailed_disease_item['option']
                        if option not in max_likelihood_option_list:
                            max_likelihood_option_list.append(option)
            max_likelihood_option_list.append('以上都不是')

        self.max_likelihood_symptom = max_likelihood_symptom
        self.max_likelihood_property = max_likelihood_property
        self.max_likelihood_option_list = max_likelihood_option_list

    def init_prob_list(self):
        latest_inquired_symptom = self.latest_inquired_symptom
        detailed_disease_list = \
            query_detailed_disease_list_by_symptom(latest_inquired_symptom)

        disease_name_list = []
        disease_freq_list = []

        disease_count_list = []

        for index, detailed_disease_item in enumerate(detailed_disease_list):
            disease = detailed_disease_item['disease']
            freq = float(detailed_disease_item['freq'])

            if disease not in disease_name_list:
                disease_name_list.append(disease)
                disease_freq_list.append(0.1 + freq)
                disease_count_list.append(1)
            else:
                cur_index = disease_name_list.index(disease)
                disease_freq_list[cur_index] += freq
                disease_count_list[cur_index] += 1

        disease_count_list = np.array(disease_count_list)
        disease_freq_list = np.array(disease_freq_list)
        disease_freq_list /= disease_count_list
        disease_freq_list /= np.sum(disease_freq_list)
        disease_freq_list = np.log(disease_freq_list)

        self.disease_name_list = disease_name_list
        self.disease_freq_list = disease_freq_list.reshape(-1)
        self.detailed_disease_list = detailed_disease_list

    def prob_calculation_by_prominent_disease(self, option):
        prominent_detailed_disease_list = self.prominent_detailed_disease_list
        latest_inquired_symptom = self.latest_inquired_symptom
        latest_inquired_property = self.latest_inquired_property
        disease_param_max_sum = 0
        prominent_detailed_item = None

        for index, detailed_disease_item in enumerate(prominent_detailed_disease_list):
            freq = float(detailed_disease_item['freq'])
            impt = float(detailed_disease_item['impt'])
            spec = float(detailed_disease_item['spec'])
            param_sum = freq + impt + spec
            if param_sum > disease_param_max_sum:
                disease_param_max_sum = param_sum
                prominent_detailed_item = detailed_disease_item

        if latest_inquired_symptom != prominent_detailed_item['symptom']:
            self.max_likelihood_symptom = prominent_detailed_item['symptom']
            self.max_likelihood_property = None
            self.max_likelihood_option_list = ['是', '否']
        else:
            if latest_inquired_property is None:
                if option == '是':
                    self.inquiry_symptom_property(prominent_detailed_item['symptom'],
                                                  prominent_detailed_item['property'],
                                                  detailed_disease_list=None)
                elif option == '否':
                    self.prob_decay_by_symptom(prominent_detailed_disease_list)
                    # 此症状已经全部问完
            else:
                print('对该属性进行概率削减')

    def get_inquired_symptom_property_list(self):
        inquired_symptom_property_list = []
        for inquiry_item in self.inquiry_list:
            symptom = inquiry_item[0]
            property = inquiry_item[1]
            if property is None:
                inquired_symptom_property_list.append({'symptom': symptom, 'property': None})
                continue
            for inquired_symptom_property_item in inquired_symptom_property_list:
                if inquired_symptom_property_item['symptom'] == symptom and \
                        inquired_symptom_property_item['property'] == property:
                    break
            else:
                inquired_symptom_property_list.append({'symptom': symptom, 'property': property})
        return inquired_symptom_property_list

    def init_prominent_detailed_disease_list(self):
        max_likelihood_disease = self.get_max_likelihood_disease()
        prominent_detailed_disease_list = \
            query_detailed_disease_list_by_disease(max_likelihood_disease)
        self.prominent_detailed_disease_list = prominent_detailed_disease_list

    @staticmethod
    def get_param_sum(detailed_disease_item):
        freq = float(detailed_disease_item['freq'])
        impt = float(detailed_disease_item['impt'])
        spec = float(detailed_disease_item['spec'])
        param_sum = freq + impt + spec
        return param_sum

    def get_disease_freq(self, disease):
        disease_name_list = self.disease_name_list
        disease_freq_list = self.disease_freq_list
        return disease_freq_list[disease_name_list.index(disease)]

    def detailed_list_switch(self):
        prominent_detailed_disease_list = self.prominent_detailed_disease_list

        if prominent_detailed_disease_list is not None:
            disease = prominent_detailed_disease_list[0]['disease']
            if self.get_disease_freq(disease) < threshold_deactivate_freq:
                self.prominent_detailed_disease_list = None
        else:
            if self.disease_freq_list is not None:
                # 某个疾病已经达到突出阈值，激活 prominent_detailed_disease_list
                if np.max(self.disease_freq_list) > threshold_activate_freq:
                    self.init_prominent_detailed_disease_list()

    def clear_details(self):
        detailed_disease_list = self.detailed_disease_list
        prominent_detailed_disease_list = self.prominent_detailed_disease_list

        new_detailed_disease_list = []
        new_prominent_detailed_disease_list = []

        inquired_symptom_property_list = self.get_inquired_symptom_property_list()
        if detailed_disease_list is not None:
            for detailed_item in detailed_disease_list:
                for inquired_item in inquired_symptom_property_list:
                    if detailed_item['symptom'] == inquired_item['symptom'] \
                            and detailed_item['property'] == inquired_item['property']:
                        break
                else:
                    new_detailed_disease_list.append(detailed_item)
            self.detailed_disease_list = new_detailed_disease_list

        if prominent_detailed_disease_list is not None:
            for prominent_item in prominent_detailed_disease_list:
                for inquired_item in inquired_symptom_property_list:
                    if prominent_item['symptom'] == inquired_item['symptom'] \
                            and prominent_item['property'] == inquired_item['property']:
                        break
                else:
                    new_prominent_detailed_disease_list.append(prominent_item)
            self.prominent_detailed_disease_list = new_prominent_detailed_disease_list

    def prob_decay_or_increase(self):
        disease_name_list = self.disease_name_list
        disease_freq_list = self.disease_freq_list

        detailed_disease_list = self.get_detailed_disease_list()

        latest_inquired_symptom = self.latest_inquired_symptom
        latest_inquired_property = self.latest_inquired_property
        option = self.latest_submit_option

        for index, detailed_disease_item in enumerate(detailed_disease_list):
            if detailed_disease_item['symptom'] == latest_inquired_symptom:
                if self.prominent_detailed_disease_list is not None:
                    if option == '是':
                        break
                    else:
                        # 不匹配的疾病概率降低
                        param_sum = self.get_param_sum(detailed_disease_item)
                        disease = detailed_disease_item['disease']
                        cur_disease_index = disease_name_list.index(disease)
                        disease_freq_list[cur_disease_index] += np.log(1 - param_sum / 15 * decay_weight)
                else:
                    if detailed_disease_item['property'] == latest_inquired_property:
                        param_sum = self.get_param_sum(detailed_disease_item)
                        if option != '以上都不是':
                            if detailed_disease_item['option'] == option:
                                # 匹配的疾病概率提升
                                disease = detailed_disease_item['disease']
                                cur_disease_index = disease_name_list.index(disease)
                                disease_freq_list[cur_disease_index] += np.log(1 + param_sum / 15 * decay_weight)
                            else:
                                # 不匹配的疾病概率降低
                                disease = detailed_disease_item['disease']
                                cur_disease_index = disease_name_list.index(disease)
                                disease_freq_list[cur_disease_index] += np.log(1 - param_sum / 15 * decay_weight)

                        else:
                            # 不匹配的疾病概率降低
                            disease = detailed_disease_item['disease']
                            cur_disease_index = disease_name_list.index(disease)
                            disease_freq_list[cur_disease_index] += np.log(1 - param_sum / 15 * decay_weight)
        self.disease_freq_list = disease_freq_list.reshape(-1)

    def prob_decay_by_symptom(self, detailed_disease_list):
        disease_name_list = self.disease_name_list
        disease_freq_list = self.disease_freq_list

        latest_inquired_symptom = self.latest_inquired_symptom

        for index, detailed_disease_item in enumerate(detailed_disease_list):
            if detailed_disease_item['symptom'] == latest_inquired_symptom:
                impt = float(detailed_disease_item['impt'])
                spec = float(detailed_disease_item['spec'])
                score = impt + spec
                # 不匹配的疾病概率降低
                disease = detailed_disease_item['disease']
                cur_disease_index = disease_name_list.index(disease)
                disease_freq_list[cur_disease_index] += np.log(1 - score / 10 * decay_weight)

        self.disease_freq_list = disease_freq_list.reshape(-1)

    def end_conversation(self):
        disease_freq_list = self.disease_freq_list

        if np.max(disease_freq_list) > threshold_top_off_min_freq:
            return True

        return self.are_we_done_with_all_list(self.detailed_disease_list)

    @staticmethod
    def are_we_done_with_all_list(detailed_disease_list):
        for detailed_disease_item in detailed_disease_list:
            if not detailed_disease_item['is_inquired']:
                return False
        else:
            return True
